﻿//========================================================= 
/**@file NesCPU.h
 * @brief CPU的不同实现
 * 
 * @date 2024-07-13   13:02:13
 * @author Zhyioo 
 * @version 1.0
 */ 
//--------------------------------------------------------- 
#ifndef _LIBZHOUYB_NESCPU_H_
#define _LIBZHOUYB_NESCPU_H_
//--------------------------------------------------------- 
#include "CPU.h"
//--------------------------------------------------------- 
namespace zhou_yb {
namespace wrapper {
namespace nes {
//--------------------------------------------------------- 
/**
 * @brief 根据NES标准方式实现的CPU
 * @date 2024-07-14 22:31:16
 */
class NesStandardCPU : public NesBasicCPU
{
protected:
    typedef void (NesStandardCPU::*nes_function)(void);

    /// CPU指令
    struct ExecuteInstruction
    {
        nes_function operate;
        nes_function addrmode;

        ExecuteInstruction(nes_function op, nes_function mode)
        {
            operate = op;
            addrmode = mode;
        }
    };
    ExecuteInstruction* _lookup_execute;
protected:
    void _OnInit()
    {
#define M(op, mode, c, op_code) ExecuteInstruction(&NesStandardCPU::##op, &NesStandardCPU::##mode)
        _lookup_execute = new ExecuteInstruction[256] {
            M(BRK, IMM, 7, 0x00), M(ORA, IZX, 6, 0x01), M(KIL, IMP, 0, 0x02), M(SLO, IZX, 8, 0x03), M(NOP, NX1, 3, 0x04), M(ORA, ZP0, 3, 0x05), M(ASL, ZP0, 5, 0x06), M(SLO, ZP0, 5, 0x07), M(PHP, IMP, 3, 0x08), M(ORA, IMM, 2, 0x09), M(ASL, IMP, 2, 0x0A), M(ANC, IMM, 2, 0x0B), M(NOP, NX2, 4, 0x0C), M(ORA, ABS, 4, 0x0D), M(ASL, ABS, 6, 0x0E), M(SLO, ABS, 6, 0x0F),
            M(BPL, REL, 2, 0x10), M(ORA, IZY, 5, 0x11), M(KIL, IMP, 0, 0x12), M(SLO, IZY, 8, 0x13), M(NOP, NX1, 4, 0x14), M(ORA, ZPX, 4, 0x15), M(ASL, ZPX, 6, 0x16), M(SLO, ZPX, 6, 0x17), M(CLC, IMP, 2, 0x18), M(ORA, ABY, 4, 0x19), M(NOP, IMP, 2, 0x1A), M(SLO, ABY, 7, 0x1B), M(NOP, ABX, 4, 0x1C), M(ORA, ABX, 4, 0x1D), M(ASL, ABX, 7, 0x1E), M(SLO, ABX, 7, 0x1F),
            M(JSR, ABS, 6, 0x20), M(AND, IZX, 6, 0x21), M(KIL, IMP, 0, 0x22), M(RLA, IZX, 8, 0x23), M(BIT, ZP0, 3, 0x24), M(AND, ZP0, 3, 0x25), M(ROL, ZP0, 5, 0x26), M(RLA, ZP0, 5, 0x27), M(PLP, IMP, 4, 0x28), M(AND, IMM, 2, 0x29), M(ROL, IMP, 2, 0x2A), M(ANC, IMM, 2, 0x2B), M(BIT, ABS, 4, 0x2C), M(AND, ABS, 4, 0x2D), M(ROL, ABS, 6, 0x2E), M(RLA, ABS, 6, 0x2F),
            M(BMI, REL, 2, 0x30), M(AND, IZY, 5, 0x31), M(KIL, IMP, 0, 0x32), M(RLA, IZY, 8, 0x33), M(NOP, ZPX, 4, 0x34), M(AND, ZPX, 4, 0x35), M(ROL, ZPX, 6, 0x36), M(RLA, ZPX, 6, 0x37), M(SEC, IMP, 2, 0x38), M(AND, ABY, 4, 0x39), M(NOP, IMP, 2, 0x3A), M(RLA, ABY, 7, 0x3B), M(NOP, ABX, 4, 0x3C), M(AND, ABX, 4, 0x3D), M(ROL, ABX, 7, 0x3E), M(RLA, ABX, 7, 0x3F),
            M(RTI, IMP, 6, 0x40), M(EOR, IZX, 6, 0x41), M(KIL, IMP, 0, 0x42), M(SRE, IZX, 8, 0x43), M(NOP, NX1, 3, 0x44), M(EOR, ZP0, 3, 0x45), M(LSR, ZP0, 5, 0x46), M(SRE, ZP0, 5, 0x47), M(PHA, IMP, 3, 0x48), M(EOR, IMM, 2, 0x49), M(LSR, IMP, 2, 0x4A), M(ALR, IMM, 2, 0x4B), M(JMP, ABS, 3, 0x4C), M(EOR, ABS, 4, 0x4D), M(LSR, ABS, 6, 0x4E), M(SRE, ABS, 6, 0x4F),
            M(BVC, REL, 2, 0x50), M(EOR, IZY, 5, 0x51), M(KIL, IMP, 0, 0x52), M(SRE, IZY, 8, 0x53), M(NOP, ZPX, 4, 0x54), M(EOR, ZPX, 4, 0x55), M(LSR, ZPX, 6, 0x56), M(SRE, ZPX, 6, 0x57), M(CLI, IMP, 2, 0x58), M(EOR, ABY, 4, 0x59), M(NOP, IMP, 2, 0x5A), M(SRE, ABY, 7, 0x5B), M(NOP, ABX, 4, 0x5C), M(EOR, ABX, 4, 0x5D), M(LSR, ABX, 7, 0x5E), M(SRE, ABX, 7, 0x5F),
            M(RTS, IMP, 6, 0x60), M(ADC, IZX, 6, 0x61), M(KIL, IMP, 0, 0x62), M(RRA, IZX, 8, 0x63), M(NOP, ZP0, 3, 0x64), M(ADC, ZP0, 3, 0x65), M(ROR, ZP0, 5, 0x66), M(RRA, ZP0, 5, 0x67), M(PLA, IMP, 4, 0x68), M(ADC, IMM, 2, 0x69), M(ROR, IMP, 2, 0x6A), M(ARR, IMM, 2, 0x6B), M(JMP, IND, 5, 0x6C), M(ADC, ABS, 4, 0x6D), M(ROR, ABS, 6, 0x6E), M(RRA, ABS, 6, 0x6F),
            M(BVS, REL, 2, 0x70), M(ADC, IZY, 5, 0x71), M(KIL, IMP, 0, 0x72), M(RRA, IZY, 8, 0x73), M(NOP, ZPX, 4, 0x74), M(ADC, ZPX, 4, 0x75), M(ROR, ZPX, 6, 0x76), M(RRA, ZPX, 6, 0x77), M(SEI, IMP, 2, 0x78), M(ADC, ABY, 4, 0x79), M(NOP, IMP, 2, 0x7A), M(RRA, ABY, 7, 0x7B), M(NOP, ABX, 4, 0x7C), M(ADC, ABX, 4, 0x7D), M(ROR, ABX, 7, 0x7E), M(RRA, ABX, 7, 0x7F),
            M(NOP, IMM, 2, 0x80), M(STA, IZX, 6, 0x81), M(NOP, IMM, 2, 0x82), M(SAX, IZX, 6, 0x83), M(STY, ZP0, 3, 0x84), M(STA, ZP0, 3, 0x85), M(STX, ZP0, 3, 0x86), M(SAX, ZP0, 3, 0x87), M(DEY, IMP, 2, 0x88), M(NOP, IMM, 2, 0x89), M(TXA, IMP, 2, 0x8A), M(XAA, IMM, 2, 0x8B), M(STY, ABS, 4, 0x8C), M(STA, ABS, 4, 0x8D), M(STX, ABS, 4, 0x8E), M(SAX, ABS, 4, 0x8F),
            M(BCC, REL, 2, 0x90), M(STA, IZY, 6, 0x91), M(KIL, IMP, 0, 0x92), M(AHX, IZY, 6, 0x93), M(STY, ZPX, 4, 0x94), M(STA, ZPX, 4, 0x95), M(STX, ZPY, 4, 0x96), M(SAX, ZPY, 4, 0x97), M(TYA, IMP, 2, 0x98), M(STA, ABY, 5, 0x99), M(TXS, IMP, 2, 0x9A), M(TAS, ABY, 5, 0x9B), M(SHY, ABX, 5, 0x9C), M(STA, ABX, 5, 0x9D), M(SHY, ABY, 5, 0x9E), M(AHX, ABY, 5, 0x9F),
            M(LDY, IMM, 2, 0xA0), M(LDA, IZX, 6, 0xA1), M(LDX, IMM, 2, 0xA2), M(LAX, IZX, 6, 0xA3), M(LDY, ZP0, 3, 0xA4), M(LDA, ZP0, 3, 0xA5), M(LDX, ZP0, 3, 0xA6), M(LAX, ZP0, 3, 0xA7), M(TAY, IMP, 2, 0xA8), M(LDA, IMM, 2, 0xA9), M(TAX, IMP, 2, 0xAA), M(LAX, IMM, 2, 0xAB), M(LDY, ABS, 4, 0xAC), M(LDA, ABS, 4, 0xAD), M(LDX, ABS, 4, 0xAE), M(LAX, ABS, 4, 0xAF),
            M(BCS, REL, 2, 0xB0), M(LDA, IZY, 5, 0xB1), M(KIL, IMP, 0, 0xB2), M(LAX, IZY, 5, 0xB3), M(LDY, ZPX, 4, 0xB4), M(LDA, ZPX, 4, 0xB5), M(LDX, ZPY, 4, 0xB6), M(LAX, ZPY, 4, 0xB7), M(CLV, IMP, 2, 0xB8), M(LDA, ABY, 4, 0xB9), M(TSX, IMP, 2, 0xBA), M(LAS, ABY, 4, 0xBB), M(LDY, ABX, 4, 0xBC), M(LDA, ABX, 4, 0xBD), M(LDX, ABY, 4, 0xBE), M(LAX, ABY, 4, 0xBF),
            M(CPY, IMM, 2, 0xC0), M(CMP, IZX, 6, 0xC1), M(NOP, IMM, 2, 0xC2), M(DCP, IZX, 8, 0xC3), M(CPY, ZP0, 3, 0xC4), M(CMP, ZP0, 3, 0xC5), M(DEC, ZP0, 5, 0xC6), M(DCP, ZP0, 5, 0xC7), M(INY, IMP, 2, 0xC8), M(CMP, IMM, 2, 0xC9), M(DEX, IMP, 2, 0xCA), M(AXS, IMM, 2, 0xCB), M(CPY, ABS, 4, 0xCC), M(CMP, ABS, 4, 0xCD), M(DEC, ABS, 6, 0xCE), M(DCP, ABS, 6, 0xCF),
            M(BNE, REL, 2, 0xD0), M(CMP, IZY, 5, 0xD1), M(KIL, IMP, 0, 0xD2), M(DCP, IZY, 8, 0xD3), M(NOP, ZPX, 4, 0xD4), M(CMP, ZPX, 4, 0xD5), M(DEC, ZPX, 6, 0xD6), M(DCP, ZPX, 6, 0xD7), M(CLD, IMP, 2, 0xD8), M(CMP, ABY, 4, 0xD9), M(NOP, IMP, 2, 0xDA), M(DCP, ABY, 7, 0xDB), M(NOP, ABX, 4, 0xDC), M(CMP, ABX, 4, 0xDD), M(DEC, ABX, 7, 0xDE), M(DCP, ABX, 7, 0xDF),
            M(CPX, IMM, 2, 0xE0), M(SBC, IZX, 6, 0xE1), M(NOP, IMM, 2, 0xE2), M(ISC, IZX, 8, 0xE3), M(CPX, ZP0, 3, 0xE4), M(SBC, ZP0, 3, 0xE5), M(INC, ZP0, 5, 0xE6), M(ISC, ZP0, 5, 0xE7), M(INX, IMP, 2, 0xE8), M(SBC, IMM, 2, 0xE9), M(NOP, IMP, 2, 0xEA), M(SBC, IMM, 2, 0xEB), M(CPX, ABS, 4, 0xEC), M(SBC, ABS, 4, 0xED), M(INC, ABS, 6, 0xEE), M(ISC, ABS, 6, 0xEF),
            M(BEQ, REL, 2, 0xF0), M(SBC, IZY, 5, 0xF1), M(KIL, IMP, 0, 0xF2), M(ISC, IZY, 8, 0xF3), M(NOP, ZPX, 4, 0xF4), M(SBC, ZPX, 4, 0xF5), M(INC, ZPX, 6, 0xF6), M(ISC, ZPX, 6, 0xF7), M(SED, IMP, 2, 0xF8), M(SBC, ABY, 4, 0xF9), M(NOP, IMP, 2, 0xFA), M(ISC, ABY, 7, 0xFB), M(NOP, ABX, 4, 0xFC), M(SBC, ABX, 4, 0xFD), M(INC, ABX, 7, 0xFE), M(ISC, ABX, 7, 0xFF),
        };
#undef M
    }
    virtual void execute(INSTRUCTION& ins)
    {
        ExecuteInstruction& exe = _lookup_execute[ins.code];
        _op_ex_clock = 0;
        _addr_ex_clock = 0;
        IdleCount = ins.cycles;
        (this->*exe.addrmode)();
        (this->*exe.operate)();
        if (_op_ex_clock && _addr_ex_clock)
            IdleCount += 1;
    }
    /* 寻址方式 */
    void NX1() 
    {
        PC += 1;
    }
    void NX2()
    {
        PC += 2;
    }
    void IMP()
    {
        _fetched = A;
    }
    void IMM()
    {
        _addr_abs = PC++;
    }
    void ZP0()
    {
        _addr_abs = _readPC();
    }
    void ZPX()
    {
        _addr_abs = _itobyte(_readPC() + X);
    }
    void ZPY()
    {
        _addr_abs = _itobyte(_readPC() + Y);
    }
    void REL()
    {
        _addr_rel = _readPC();
        if (_addr_rel & 0x80)
        {
            _addr_rel |= 0xFF00;
        }
    }
    void ABS()
    {
        _addr_abs = _readPC_Word();
    }
    void ABX()
    {
        ushort addr = _readPC_Word();
        _addr_abs = addr + X;

        if (_is_overflow(addr, _addr_abs))
        {
            _addr_ex_clock = 1;
        }
    }
    void ABY()
    {
        ushort addr = _readPC_Word();
        _addr_abs = addr + Y;

        if (_is_overflow(addr, _addr_abs))
        {
            _addr_ex_clock = 1;
        }
    }
    void IND()
    {
        ushort ptr = _readPC_Word();
        if ((ptr & 0xFF) == 0xFF)
        {
            _addr_abs = (_bus->Read(ptr & 0xFF00) << 8) | _bus->Read(ptr);
        }
        else
        {
            _addr_abs = (_bus->Read(ptr + 1) << 8) | _bus->Read(ptr);
        }
    }
    void IZX()
    {
        _addr_abs = _readIZ(_readPC() + X);
    }
    void IZY()
    {
        _addr_abs = _readIZ(_readPC());
        _addr_ex_clock = _is_overflow(_addr_abs + Y, _addr_abs);
        _addr_abs += Y;
    }
    /* 操作指令 */
    void ALR()
    {
        AND();
        fetch();
        LSR();
        _op_ex_clock = 1;
    }
    void RLA()
    {
        ROL();
        fetch();
        AND();
        _op_ex_clock = 0;
    }
    void AHX()
    {
    }
    void TAS()
    {
    }
    void SHY()
    {
    }
    void LAS()
    {
    }
    void DCP()
    {
        DEC();
        fetch();
        CMP();
        _op_ex_clock = 0;
    }
    void AXS()
    {
        X = A & X;
        _set_ZN(X);
    }
    void ISC()
    {
        INC();
        fetch();
        SBC();
        _op_ex_clock = 0;
    }
    void SRE()
    {
        LSR();
        fetch();
        EOR();
        _op_ex_clock = 0;
    }
    void ADC()
    {
        fetch();
        ushort tmp = A + _fetched + Status.C;
        Status.C = ToBOOL(tmp > 0xFF);
        Status.V = ToBOOL((~(A ^ _fetched)) & ((A ^ tmp) & 0x80));
        A = _itobyte(tmp);
        _set_ZN(A);
        _op_ex_clock = 1;
    }
    void ARR()
    {
        fetch();
        A = A & _fetched;

        byte tmp = (_fetched >> 1) | (Status.C << 7);
        byte bit5 = (tmp & 0x20);
        byte bit6 = (tmp & 0x40);
        Status.C = ToBOOL(bit5);
        Status.V = ToBOOL(bit5 ^ bit6);
        _set_ZN(tmp);
        _write_A(tmp);
    }
    void SBC()
    {
        fetch();
        byte v = _fetched ^ 0xFF;
        ushort tmp = A + v + Status.C;
        Status.C = ToBOOL(tmp > 0xFF);
        Status.V = ToBOOL(((tmp ^ A) & (tmp ^ v) & 0x80));
        A = _itobyte(tmp);
        _set_ZN(A);
    }
    void ANC()
    {
        AND();
        Status.C = ToBOOL(A & Status.N);
        _op_ex_clock = 1;
    }
    void AND()
    {
        fetch();
        A = A & _fetched;
        _set_ZN(A);
        _op_ex_clock = 1;
    }
    void ASL()
    {
        fetch();
        ushort tmp = _fetched << 1;
        Status.C = ToBOOL(tmp > 0xFF);

        byte v = _itobyte(tmp);
        _set_ZN(v);
        _write_A(v);
    }
    void B_if(bool is_true)
    {
        if (!is_true)
            return;
        uint addr = PC + _addr_rel;
        if (_is_overflow(PC, addr))
            IdleCount += 1;
        _addr_abs = addr;
        PC = addr;
        IdleCount += 1;
    }
    void BCC()
    {
        B_if(Status.C == 0);
    }
    void BCS()
    {
        B_if(Status.C == 1);
    }
    void BEQ()
    {
        B_if(Status.Z == 1);
    }
    void BMI()
    {
        B_if(Status.N == 1);
    }
    void BNE()
    {
        B_if(Status.Z == 0);
    }
    void BPL()
    {
        B_if(Status.N == 0);
    }
    void BVC()
    {
        B_if(Status.V == 0);
    }
    void BVS()
    {
        B_if(Status.V == 1);
    }
    void BIT()
    {
        fetch();
        byte tmp = A & _fetched;
        Status.Z = ToBOOL(tmp == 0);
        Status.N = ToBOOL(_fetched & 0x80);
        Status.V = ToBOOL(_fetched & 0x40);
    }
    void BRK()
    {
        PC += 1;
        Status.I = 1;
        _writeSP_Word(PC);

        Status.B = 1;
        _writeSP(Status.value);
        Status.B = 0;

        PC = MapperHelper::ReadWord(*_bus, CpuAddress::IRQ);
    }
    void CLC()
    {
        Status.C = 0;
    }
    void CLD()
    {
        Status.D = 0;
    }
    void CLI()
    {
        Status.I = 0;
    }
    void CLV()
    {
        Status.V = 0;
    }
    void C_with(byte v)
    {
        fetch();
        byte tmp = v - _fetched;
        Status.C = ToBOOL(v >= _fetched);
        _set_ZN(tmp);
        _op_ex_clock = 1;
    }
    void CMP()
    {
        C_with(A);
    }
    void CPX()
    {
        C_with(X);
    }
    void CPY()
    {
        C_with(Y);
    }
    void DEC()
    {
        fetch();
        byte tmp = _fetched - 1;
        _bus->Write(_addr_abs, tmp);
        _set_ZN(tmp);
    }
    void DEX()
    {
        X = X - 1;
        _set_ZN(X);
    }
    void DEY()
    {
        Y = Y - 1;
        _set_ZN(Y);
    }
    void EOR()
    {
        fetch();
        A = A ^ _fetched;
        _set_ZN(A);
        _op_ex_clock = 1;
    }
    void INC()
    {
        fetch();
        byte tmp = _fetched + 1;
        _bus->Write(_addr_abs, tmp);
        _set_ZN(tmp);
    }
    void INX()
    {
        X = X + 1;
        _set_ZN(X);
    }
    void INY()
    {
        Y = Y + 1;
        _set_ZN(Y);
    }
    void JMP()
    {
        PC = _addr_abs;
    }
    void JSR()
    {
        PC -= 1;
        _writeSP_Word(PC);
        PC = _addr_abs;
    }
    void LDA()
    {
        A = fetch();
        _set_ZN(A);
        _op_ex_clock = 1;
    }
    void KIL()
    {

    }
    void LAX()
    {
        fetch();
        X = _fetched;
        A = _fetched;
        _set_ZN(_fetched);
        _op_ex_clock = 1;
    }
    void LDX()
    {
        X = fetch();
        _set_ZN(X);
        _op_ex_clock = 1;
    }
    void LDY()
    {
        Y = fetch();
        _set_ZN(Y);
        _op_ex_clock = 1;
    }
    void LSR()
    {
        fetch();
        Status.C = ToBOOL(_fetched & 0x01);
        byte tmp = (_fetched >> 1);
        _set_ZN(tmp);
        _write_A(tmp);
    }
    void NOP()
    {
        const byte nop_code[] = { 0x1C, 0x3C, 0x5C, 0x7C, 0xDC, 0xFC };
        for (int i = 0;i < 6; ++i)
        {
            if (_opcode == nop_code[i])
            {
                _op_ex_clock = 1;
                break;
            }
        }
    }
    void ORA()
    {
        fetch();
        A = A | _fetched;
        _set_ZN(A);
        _op_ex_clock = 1;
    }
    void PHA()
    {
        _writeSP(A);
    }
    void PHP()
    {
        CpuStatus s = Status;
        s.B = 1;
        s.U = 1;
        _writeSP(s.value);

        Status.B = 0;
        Status.U = 0;
    }
    void PLA()
    {
        A = _readSP();
        _set_ZN(A);
    }
    void PLP()
    {
        Status.value = _readSP();
        Status.B = 0;
        Status.I = 1;
        //Status.U = 1;
    }
    void ROL()
    {
        fetch();
        ushort tmp = (_fetched << 1) | Status.C;
        Status.C = ToBOOL(tmp > 0xFF);

        byte v = _itobyte(tmp);
        _set_ZN(v);
        _write_A(v);
    }
    void ROR()
    {
        fetch();
        byte tmp = (_fetched >> 1) | (Status.C << 7);
        Status.C = ToBOOL(_fetched & 0x01);
        _set_ZN(tmp);
        _write_A(tmp);
    }
    void RRA()
    {
        ROR();
        fetch();
        ADC();
        _op_ex_clock = 0;
    }
    void RTI()
    {
        Status.value = _readSP();
        Status.B = 0;
        Status.U = 0;
        PC = _readSP_Word();
    }
    void RTS()
    {
        PC = _readSP_Word();
        PC += 1;
    }
    void SLO()
    {
        ASL();
        fetch();
        ORA();
        _op_ex_clock = 0;
    }
    void SEC()
    {
        Status.C = 1;
    }
    void SED()
    {
        Status.D = 1;
    }
    void SEI()
    {
        Status.I = 1;
    }
    void STA()
    {
        _bus->Write(_addr_abs, A);
    }
    void STX()
    {
        _bus->Write(_addr_abs, X);
    }
    void SAX()
    {
        _bus->Write(_addr_abs, A & X);
    }
    void STY()
    {
        _bus->Write(_addr_abs, Y);
    }
    void TAX()
    {
        X = A;
        _set_ZN(X);
    }
    void TAY()
    {
        Y = A;
        _set_ZN(Y);
    }
    void TSX()
    {
        X = SP;
        _set_ZN(X);
    }
    void TXA()
    {
        A = X;
        _set_ZN(A);
    }
    void XAA()
    {

    }
    void TXS()
    {
        SP = X;
    }
    void TYA()
    {
        A = Y;
        _set_ZN(A);
    }
public:
    NesStandardCPU() : NesBasicCPU()
    {
        _OnInit();
    }
};
//--------------------------------------------------------- 
/**
 * @brief 特化实现的CPU版本
 * 
 * 2024-07-15 13:23:26
 */ 
class NesCPU : public NesBasicCPU
{
protected:
    byte* _ram_memory = nullptr;
    IMapper* _prg = nullptr;
protected:
    virtual inline byte _readPC()
    {
        ushort r_PC = PC++;
        if (r_PC < 0x8000)
            return _ram_memory[r_PC];
        return _prg->Read(r_PC - 0x8000);
    }
    virtual inline byte _readSP()
    {
        ++SP;
        return _ram_memory[CpuAddress::SP + SP];
    }
    virtual inline void _writeSP(byte v)
    {
        _ram_memory[CpuAddress::SP + SP] = v;
        --SP;
    }

    /// 寻址方式处理
    void handle_address(const byte& mode)
    {
        switch (mode)
        {
        case byABS:
            _addr_abs = _readPC_Word();
            break;
        case byIMP:
            _fetched = A;
            break;
        case byNX1:
            PC += 1;
            break;
        case byNX2:
            PC += 2;
            break;
        case byIMM:
            _addr_abs = PC++;
            break;
        case byZP0:
            _addr_abs = _readPC();
            break;
        case byZPX:
            _addr_abs = _itobyte(_readPC() + X);
            break;
        case byZPY:
            _addr_abs = _itobyte(_readPC() + Y);
            break;
        case byREL:
            _addr_rel = _readPC();
            if (_addr_rel & 0x80)
            {
                _addr_rel |= 0xFF00;
            }
            break;
        case byABX:
            {
                ushort addr = _readPC_Word();
                _addr_abs = addr + X;

                if (_is_overflow(addr, _addr_abs))
                {
                    _addr_ex_clock = 1;
                }
            }
            break;
        case byABY:
            {
                ushort addr = _readPC_Word();
                _addr_abs = addr + Y;

                if (_is_overflow(addr, _addr_abs))
                {
                    _addr_ex_clock = 1;
                }
            }
            break;
        case byIND:
            {
                ushort ptr = _readPC_Word();
                if ((ptr & 0xFF) == 0xFF)
                {
                    _addr_abs = (_bus->Read(ptr & 0xFF00) << 8) | _bus->Read(ptr);
                }
                else
                {
                    _addr_abs = (_bus->Read(ptr + 1) << 8) | _bus->Read(ptr);
                }
            }
            break;
        case byIZX:
            _addr_abs = _readIZ(_readPC() + X);
            break;
        case byIZY:
            _addr_abs = _readIZ(_readPC());
            _addr_ex_clock = _is_overflow(_addr_abs + Y, _addr_abs);
            _addr_abs += Y;
            break;
        }
    }
    /// 操作指令处理
    void handle_operate(const byte& mode)
    {
#define __B_if(v) \
        if (v) \
        { \
            uint addr = PC + _addr_rel; \
            if (_is_overflow(PC, addr)) \
                IdleCount += 1; \
            _addr_abs = addr; \
            PC = addr; \
            IdleCount += 1; \
        }

#define __C_with(v) \
    { \
        fetch(); \
        byte tmp = v - _fetched; \
        Status.C = ToBOOL(v >= _fetched); \
        _set_ZN(tmp); \
        _op_ex_clock = 1; \
    }

        switch (mode)
        {
        case byALR:
            handle_operate(byAND);
            fetch();
            handle_operate(byLSR);
            _op_ex_clock = 1;
            break;
        case byRLA:
            handle_operate(byROL);
            fetch();
            handle_operate(byAND);
            _op_ex_clock = 0;
            break;
        case byDCP:
            handle_operate(byDEC);
            fetch();
            handle_operate(byCMP);
            _op_ex_clock = 0;
            break;
        case byAXS:
            X = A & X;
            _set_ZN(X);
            break;
        case byISC:
            handle_operate(byINC);
            fetch();
            handle_operate(bySBC);
            _op_ex_clock = 0;
            break;
        case bySRE:
            handle_operate(byLSR);
            fetch();
            handle_operate(byEOR);
            _op_ex_clock = 0;
            break;
        case byADC:
            {
                fetch();
                ushort tmp = A + _fetched + Status.C;
                Status.C = ToBOOL(tmp > 0xFF);
                Status.V = ToBOOL((~(A ^ _fetched)) & ((A ^ tmp) & 0x80));
                A = _itobyte(tmp);
                _set_ZN(A);
                _op_ex_clock = 1;
            }
            break;
        case byARR:
            {
                fetch();
                A = A & _fetched;

                byte tmp = (_fetched >> 1) | (Status.C << 7);
                byte bit5 = (tmp & 0x20);
                byte bit6 = (tmp & 0x40);
                Status.C = ToBOOL(bit5);
                Status.V = ToBOOL(bit5 ^ bit6);
                _set_ZN(tmp);
                _write_A(tmp);
            }
            break;
        case bySBC:
            {
                fetch();
                byte v = _fetched ^ 0xFF;
                ushort tmp = A + v + Status.C;
                Status.C = ToBOOL(tmp > 0xFF);
                Status.V = ToBOOL(((tmp ^ A) & (tmp ^ v) & 0x80));
                A = _itobyte(tmp);
                _set_ZN(A);
            }
            break;
        case byANC:
            handle_operate(byAND);
            Status.C = ToBOOL(A & Status.N);
            _op_ex_clock = 1;
            break;
        case byAND:
            fetch();
            A = A & _fetched;
            _set_ZN(A);
            _op_ex_clock = 1;
            break;
        case byASL:
            {
                fetch();
                ushort tmp = _fetched << 1;
                Status.C = ToBOOL(tmp > 0xFF);

                byte v = _itobyte(tmp);
                _set_ZN(v);
                _write_A(v);
            }
            break;
        case byBCC:
            __B_if(Status.C == 0);
            break;
        case byBCS:
            __B_if(Status.C == 1);
            break;
        case byBEQ:
            __B_if(Status.Z == 1);
            break;
        case byBMI:
            __B_if(Status.N == 1);
            break;
        case byBNE:
            __B_if(Status.Z == 0);
            break;
        case byBPL:
            __B_if(Status.N == 0);
            break;
        case byBVC:
            __B_if(Status.V == 0);
            break;
        case byBVS:
            __B_if(Status.V == 1);
            break;
        case byBIT:
            {
                fetch();
                byte tmp = A & _fetched;
                Status.Z = ToBOOL(tmp == 0);
                Status.N = ToBOOL(_fetched & 0x80);
                Status.V = ToBOOL(_fetched & 0x40);
            }
            break;
        case byBRK:
            PC += 1;
            Status.I = 1;
            _writeSP_Word(PC);

            Status.B = 1;
            _writeSP(Status.value);
            Status.B = 0;

            PC = MapperHelper::ReadWord(*_bus, CpuAddress::IRQ);
            break;
        case byCLC:
            Status.C = 0;
            break;
        case byCLD:
            Status.D = 0;
            break;
        case byCLI:
            Status.I = 0;
            break;
        case byCLV:
            Status.V = 0;
            break;
        case byCMP:
            __C_with(A);
            break;
        case byCPX:
            __C_with(X);
            break;
        case byCPY:
            __C_with(Y);
            break;
        case byDEC:
            {
                fetch();
                byte tmp = _fetched - 1;
                _bus->Write(_addr_abs, tmp);
                _set_ZN(tmp);
            }
            break;
        case byDEX:
            X = X - 1;
            _set_ZN(X);
            break;
        case byDEY:
            Y = Y - 1;
            _set_ZN(Y);
            break;
        case byEOR:
            fetch();
            A = A ^ _fetched;
            _set_ZN(A);
            _op_ex_clock = 1;
            break;
        case byINC:
            {
                fetch();
                byte tmp = _fetched + 1;
                _bus->Write(_addr_abs, tmp);
                _set_ZN(tmp);
            }
            break;
        case byINX:
            X = X + 1;
            _set_ZN(X);
            break;
        case byINY:
            Y = Y + 1;
            _set_ZN(Y);
            break;
        case byJMP:
            PC = _addr_abs;
            break;
        case byJSR:
            PC -= 1;
            _writeSP_Word(PC);
            PC = _addr_abs;
            break;
        case byLDA:
            A = fetch();
            _set_ZN(A);
            _op_ex_clock = 1;
            break;
        case byLAX:
            fetch();
            X = _fetched;
            A = _fetched;
            _set_ZN(_fetched);
            _op_ex_clock = 1;
            break;
        case byLDX:
            X = fetch();
            _set_ZN(X);
            _op_ex_clock = 1;
            break;
        case byLDY:
            Y = fetch();
            _set_ZN(Y);
            _op_ex_clock = 1;
            break;
        case byLSR:
            {
                fetch();
                Status.C = ToBOOL(_fetched & 0x01);
                byte tmp = (_fetched >> 1);
                _set_ZN(tmp);
                _write_A(tmp);
            }
            break;
        case byNOP:
            {
                const byte nop_code[] = { 0x1C, 0x3C, 0x5C, 0x7C, 0xDC, 0xFC };
                for (int i = 0;i < 6; ++i)
                {
                    if (_opcode == nop_code[i])
                    {
                        _op_ex_clock = 1;
                        break;
                    }
                }
            }
            break;
        case byORA:
            fetch();
            A = A | _fetched;
            _set_ZN(A);
            _op_ex_clock = 1;
            break;
        case byPHA:
            _writeSP(A);
            break;
        case byPHP:
            {
                CpuStatus s = Status;
                s.B = 1;
                s.U = 1;
                _writeSP(s.value);

                Status.B = 0;
                Status.U = 0;
            }
            break;
        case byPLA:
            A = _readSP();
            _set_ZN(A);
            break;
        case byPLP:
            Status.value = _readSP();
            Status.B = 0;
            Status.I = 1;
            //Status.U = 1;
            break;
        case byROL:
            {
                fetch();
                ushort tmp = (_fetched << 1) | Status.C;
                Status.C = ToBOOL(tmp > 0xFF);

                byte v = _itobyte(tmp);
                _set_ZN(v);
                _write_A(v);
            }
            break;
        case byROR:
            {
                fetch();
                byte tmp = (_fetched >> 1) | (Status.C << 7);
                Status.C = ToBOOL(_fetched & 0x01);
                _set_ZN(tmp);
                _write_A(tmp);
            }
            break;
        case byRRA:
            handle_operate(byROR);
            fetch();
            handle_operate(byADC);
            _op_ex_clock = 0;
            break;
        case byRTI:
            Status.value = _readSP();
            Status.B = 0;
            Status.U = 0;
            PC = _readSP_Word();
            break;
        case byRTS:
            PC = _readSP_Word();
            PC += 1;
            break;
        case bySLO:
            handle_operate(byASL);
            fetch();
            handle_operate(byORA);
            _op_ex_clock = 0;
            break;
        case bySEC:
            Status.C = 1;
            break;
        case bySED:
            Status.D = 1;
            break;
        case bySEI:
            Status.I = 1;
            break;
        case bySTA:
            _bus->Write(_addr_abs, A);
            break;
        case bySTX:
            _bus->Write(_addr_abs, X);
            break;
        case bySAX:
            _bus->Write(_addr_abs, A & X);
            break;
        case bySTY:
            _bus->Write(_addr_abs, Y);
            break;
        case byTAX:
            X = A;
            _set_ZN(X);
            break;
        case byTAY:
            Y = A;
            _set_ZN(Y);
            break;
        case byTSX:
            X = SP;
            _set_ZN(X);
            break;
        case byTXA:
            A = X;
            _set_ZN(A);
            break;
        case byTXS:
            SP = X;
            break;
        case byTYA:
            A = Y;
            _set_ZN(A);
            break;
        }
#undef __B_if
#undef __C_with
    }

    virtual void execute(INSTRUCTION& ins)
    {
        handle_address(ins.addrmode_code);
        handle_operate(ins.operate_code);
    }
public:
    void SetMapper(RawMapper& ram, IMapper& prg)
    {
        _ram_memory = const_cast<byte*>(ram.GetBuffer());
        _prg = &prg;
    }
};
//--------------------------------------------------------- 
} // namespace nes
} // namespace wrapper
} // namespace zhou_yb
//--------------------------------------------------------- 
#endif // _LIBZHOUYB_NESCPU_H_
//========================================================= 
